﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace SPHelper
{
	public class Xml
	{
		/// <summary>
		/// Get an XML document from an online XML Feed
		/// </summary>
		/// <param name="url"></param>
		/// <param name="timeout">In miliseconds, the default is 1 minute</param>
		/// <returns></returns>
		public static XmlDocument GetXmlDocument(string url, int? timeout)
		{
			XmlDocument xml = new XmlDocument();
			try
			{
				// init
				// if (!SPHelper.Validate.IsValidAbsoluteUrl(url, false)) { throw new Exception("URL passed is invalid: " + url); }
				if (!SPHelper.Utilities.IsValidAbsoluteUrl(url, false)) { throw new Exception("URL passed is invalid: " + url); }
				if (timeout == null) { timeout = 60000; } //6 0 seconds


				// call url and get xml
				if (string.IsNullOrEmpty(url)) { throw new Exception("No URL passed to http request"); }
				System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
				request.AllowAutoRedirect = false;	// dont allow redirect, i.e., login prompt
				request.Timeout = (int)timeout;
				using (System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse())
				{
					xml.Load(response.GetResponseStream());
					if (xml == null) { throw new Exception("Failed to load XML using XML url: " + url); }
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("5eiuj5eh65rtsewewrwe3", ex, "url: " + url);
				xml = null;
			}
			return xml;
		}


		/// <summary>
		/// Replace xml/html element (tag) with new xml. Can either replace the element contents, or the whole element
		/// </summary>
		/// <param name="sourceXml">The source xml with element to update</param>
		/// <param name="newXml">the new xml to replace with</param>
		/// <param name="elementName">CASE SENSATIVE: xml element/tag to replace. Example name is "div", "span", "p", or "td"</param>
		/// <param name="elementId">id="" unique id for the element, this way don't replace all "div" for example. If null, will replace all elements with tag name passed</param>
		/// <param name="isReplaceEntireElement">If true, will replace the element and its contents (outerXml) Otherwise, replace just the element's contents (InnerXml)</param>
		/// <returns></returns>
		public static string ReplaceElements(string sourceXml, string newXml, string elementName, string elementId, bool isReplaceEntireElement, ref int numberElementsAffected)
		{
			try
			{
				// init
				if (string.IsNullOrEmpty(elementName)) { throw new Exception("No element name passed"); }
				numberElementsAffected = 0;


				if (string.IsNullOrEmpty(sourceXml)) { sourceXml = newXml; }
				else
				{
					// convert source into XML doc
					XmlDocument doc = new XmlDocument();
					doc.LoadXml(sourceXml);

					// get element(s)
					string xPath = "//" + elementName;
					if (!string.IsNullOrEmpty(elementId)) { xPath += "[@id='" + elementId + "']"; }
					XmlNodeList elements = doc.SelectNodes(xPath);
					if (elements == null || elements.Count == 0) { throw new Exception("Failed to get elements in source xml using xpath: " + xPath); }

					foreach (XmlNode n in elements)
					{
						if (!isReplaceEntireElement) { n.InnerXml = newXml; }
						else
						{
							XmlDocumentFragment fragment = doc.CreateDocumentFragment();
							fragment.InnerXml = newXml;
							n.ParentNode.ReplaceChild(fragment, n);
						}
						numberElementsAffected++;
					}
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("tduiitydhrer5", ex, "element name: " + elementName + " elementId: " + elementId +
					" source xml: " + sourceXml + " new xml: " + newXml);
			}
			return sourceXml;
		}

		/// <summary>
		/// Get matching xml node elements in passed xml text
		/// </summary>
		/// <param name="sourceXml">xml or html source</param>
		/// <param name="elementName">xml node name, such as "div", "span", "p", etc.</param>
		/// <param name="elementId">the value of the "id" attribute. If null, then gets all elements that match element name</param>
		/// <returns></returns>
		public static XmlNodeList GetElements(string sourceXml, string elementName, string elementId)
		{
			XmlNodeList elements = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(elementName)) { throw new Exception("No element name passed"); }


				if (!string.IsNullOrEmpty(sourceXml))
				{
					// convert source into XML doc
					XmlDocument doc = new XmlDocument();
					doc.LoadXml(sourceXml);

					// get element(s)
					string xPath = "//" + elementName;
					if (!string.IsNullOrEmpty(elementId)) { xPath += "[@id='" + elementId + "']"; }
					elements = doc.SelectNodes(xPath);
					if (elements == null || elements.Count == 0) { throw new Exception("Failed to get elements in source xml using xpath: " + xPath); }
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("tduiitydhrer5", ex, "element name: " + elementName + " elementId: " + elementId +
					" source html: " + sourceXml);
			}
			return elements;
		}

		public static string XMLDocumentToString(XmlDocument document, bool removePreProccessor)
		{
			string xmlString;
			using (var stringWriter = new StringWriter())
			using (var xmlTextWriter = XmlWriter.Create(stringWriter))
			{
				document.WriteTo(xmlTextWriter);
				xmlTextWriter.Flush();
				xmlString = stringWriter.GetStringBuilder().ToString();
				if (removePreProccessor)
				{
					int preprocessorend = xmlString.IndexOf('>');
					xmlString = xmlString.Substring(preprocessorend + 1);
				}
			}
			return xmlString;
		}


        public static string XMLElementToString(XmlElement document,bool removePreProccessor)
		{
			string xmlString;
			using (var stringWriter = new StringWriter())
			using (var xmlTextWriter = XmlWriter.Create(stringWriter))
			{
				document.WriteTo(xmlTextWriter);
				xmlTextWriter.Flush();
				xmlString = stringWriter.GetStringBuilder().ToString();
                if (removePreProccessor)
                {
                    int preprocessorend = xmlString.IndexOf('>');
                    xmlString = xmlString.Substring(preprocessorend + 1);
                }
			}
			return xmlString;
		}
	}
}
